using System;
using DL.Tools;
using UnityEngine;

namespace DL.Movement.Base
{
    //重力
    //地面检测
    //坡面检测
    //移动接口
    //有些参数我们想让他看见，所以继承了MonoBehaviour
    [RequireComponent(typeof(CharacterController))]
    public abstract class CharacterMoveControllerBase : MonoBehaviour
    {
        protected Animator m_Animator;
        protected CharacterController m_CC;
        protected OnGroundedSensor m_GroundedSensor;
        protected Transform m_Self;

        //重力检测
        //重力使用
        [SerializeField, Header("重力")] 
        private bool m_IsUseGravity;
        [field: SerializeField] protected float m_CharacterGravity { get; set; } = -9.8f;
        [field: SerializeField, Range(0f, 10f)]
        public float m_CharacterGravityMult { get; protected set; } = 1f;
        public float CharacterGravity => m_CharacterGravity * m_CharacterGravityMult;
        private float m_FallOutDeltaTime = 0.1f; //防止角色下楼被判断为下坠的时间
        [SerializeField] private Vector2 m_VerticalSpeedClamp = new(-50f, 20f);
        private float m_FallOutDeltaTimer; //下坠计时器

        [field: SerializeField, Tooltip("是否处于下落状态")]
        public bool IsFalling { get; protected set; }

        //地面检测
        [field:SerializeField, Header("地面检测")] 
        public bool CharacterIsOnGround { get; protected set; }
        //地面检测事件
        public Action<bool> CharacterIsOnGroundEvent;
        
        [SerializeField] 
        private float m_DetectionRange; //检测范围
        [SerializeField] 
        private float m_GroundDetectionOnPositionOffset; //偏移量
        [SerializeField] 
        private LayerMask m_GroundLayer;

        //碰头检测
        [field:SerializeField, Header("碰头检测")] 
        public bool CharacterHeadImpact { get; protected set; }
        [SerializeField] 
        private float m_HeadDetectionRange; //检测范围
        [SerializeField] 
        private float m_HeadDetectionOnPositionOffset; //偏移量
        
        //坡面检测
        [SerializeField, Header("坡面检测")] 
        private float m_SlopDetectionLength = 1.5f;
        [SerializeField, Tooltip("障碍物层级")]
        private LayerMask m_WhatIsObs;
       
        //临时变量,用于速度赋值
        protected Vector3 m_MovementDirection;
        protected Vector3 m_VerticalDirection;

        [field: SerializeField, Header("水平速度")]
        public BindableProperty<float> CurrentMovementVelocity { get; protected set; }

        [field:SerializeField, Header("垂直速度")]
        public BindableProperty<float> CurrentVerticalVelocity { get; protected set; }

        [field:SerializeField, Header("启用根运动")]
        public bool EnableRootMotion { get; set; }

        //插值阈值
        [Tooltip("速度修改插值阈值")]
        public float InterpolateEpsilon = 0.01f;
        [Tooltip("增速插值")]
        public float StableMovementSharpnessUp = 4f;
        [Tooltip("减速速插值")]
        public float StableMovementSharpnessDown = 6f;
        
        /// <summary>
        /// 忽视重力计时器
        /// </summary>
        private float IgnoreGravityTimer;
        protected virtual void Init()
        {
            m_FallOutDeltaTimer = m_FallOutDeltaTime;
            m_SlopDetectionLength = m_CC.height * 2;
            CurrentMovementVelocity = new();
            CurrentVerticalVelocity = new();
            m_IsUseGravity = true;
        }

        // protected virtual void OnAnimatorMove()
        // {
        //     if(!EnableRootMotion) return;
        //     m_Animator.ApplyBuiltinRootMotion();
        //     CharacterMoveInterface(m_Animator.deltaPosition);
        //     // Debug.Log("水平速度" + m_Animator.velocity);
        // }


        /// <summary>
        /// 地面检测,必要
        /// </summary>
        protected bool GroundDetecion()
        {
            // var position = m_Self.position;
            // var detectionPos = new Vector3(position.x,
            //     position.y - m_GroundDetectionOnPositionOffset, position.z);
            var oldState = CharacterIsOnGround;
            // var newState = Physics.CheckSphere(detectionPos, m_DetectionRange, m_GroundLayer,
            //     QueryTriggerInteraction.Ignore);
            var newState = m_GroundedSensor.GetIsGrounded();
            if (newState != oldState)
            {
                CharacterIsOnGroundEvent?.Invoke(newState);
            }
            return newState;
        }

        /// <summary>
        /// 尝试获取接触地面的法线,没有则返回自己的Up
        /// </summary>
        /// <returns></returns>
        public Vector3 TryGetGroundNormal()
        {
            if (Physics.Raycast(m_Self.position + (Vector3.up * .3f), Vector3.down, out var hit, m_SlopDetectionLength,
                    m_GroundLayer, QueryTriggerInteraction.Ignore))
            {
                return hit.normal;
            }
            return m_Self.up;
        }

        /// <summary>
        /// 坡面检测
        /// 将传入方向转化为贴近地面可移动方向
        /// 防止下坡过快变成弹力球
        /// </summary>
        /// <param name="moveDirection"></param>
        /// <param name="surfaceNormal"></param>
        protected Vector3 ResetVelocityOnSlop(Vector3 moveDirection,out Vector3 surfaceNormal)
        {
            if (Physics.Raycast(m_Self.position + (Vector3.up * .3f), Vector3.down,out var hit, m_SlopDetectionLength,
                    m_GroundLayer, QueryTriggerInteraction.Ignore))
            {
                // Debug.Log("射线打到物体" + hit.transform.name);
                surfaceNormal = hit.normal;
                // var dot = Vector3.Dot(Vector3.up, hit.normal);
                // //不在天花板和角色在坡面上时
                // if (CurrentVerticalVelocity.Value < 0 && !dot.Approx(0))
                // {
                //     return Vector3.ProjectOnPlane(moveDirection, hit.normal);
                // }
                var inputRight = Vector3.Cross(moveDirection, m_Self.up);
                moveDirection = Vector3.Cross(surfaceNormal, inputRight).normalized;
                return moveDirection;
            }
            //没有打到任何物体，向上
            surfaceNormal = Vector3.up;
            return moveDirection;
        }

        /// <summary>
        /// 再一次检测坡度方向
        /// </summary>
        /// <param name="moveDirection"></param>
        /// <param name="surfaceNormal"></param>
        /// <returns></returns>
        protected virtual Vector3 GetSlopMovementDirection(Vector3 moveDirection, Vector3 surfaceNormal)
        {
            var slopAngle = Vector3.Angle(surfaceNormal, Vector3.up);
            // Debug.Log("计算斜坡角度：" + slopAngle);
            if (slopAngle > 5f && slopAngle < 45f)
            {
                return Vector3.ProjectOnPlane(moveDirection, surfaceNormal).normalized;
            }
            return moveDirection.normalized;
        }


        // 缓存水平速度
        private const int Chachesize = 5;
        private Vector3[] m_VelCache = new Vector3[Chachesize];
        private int m_CurrentCacheIndex;
        private Vector3 m_AvarageVelocity;
        public Vector3 avarageVelocity => m_AvarageVelocity;

        protected Vector3 AvarageVel(Vector3 vel)
        {
            m_VelCache[m_CurrentCacheIndex] = vel;
            m_CurrentCacheIndex = (m_CurrentCacheIndex + 1) % Chachesize;
            var sult = Vector3.zero;
            foreach (var val in m_VelCache)
            {
                sult += val;
            }
            return sult / Chachesize;
        }

        /// <summary>
        /// 使用传入的速度进行一次模拟运动，如果没有碰到障碍物则代表一个移动
        /// </summary>
        /// <param name="dir"></param>
        /// <param name="moveSpeed"></param>
        /// <returns></returns>
        protected bool CanAnimationMotion(Vector3 dir,float moveSpeed)
        {
            return !Physics.Raycast(transform.position + transform.up * .5f,
                moveSpeed * Time.fixedDeltaTime * dir.normalized, out var hit, 1f, m_WhatIsObs);
        }
        
        /// <summary>
        /// CanAnimationMotion 重载版本，moveDirection 携带了速度
        /// </summary>
        /// <param name="moveDirection"></param>
        /// <returns></returns>
        protected bool CanAnimationMotion(Vector3 moveDirection)
        {
            return Physics.Raycast(transform.position + transform.up * .5f,
                Time.fixedDeltaTime * moveDirection, out var hit, 1f, m_WhatIsObs);
        }

        protected bool HeadImpactCheck()
        {
            var position = m_Self.position;
            var detectionPos = new Vector3(position.x,
                position.y + m_HeadDetectionOnPositionOffset, position.z);
            return Physics.CheckSphere(detectionPos, m_HeadDetectionRange, m_WhatIsObs,
                QueryTriggerInteraction.Ignore);
        }

        /// <summary>
        /// 水平速度，考虑插值
        /// </summary>
        /// <param name="moveDirection"></param>
        /// <param name="moveSpeed"></param>
        /// <param name="isLerp"></param>
        public virtual void CharacterMoveInterface(Vector3 moveDirection, float moveSpeed, bool isLerp = true)
        {
            m_MovementDirection = ResetVelocityOnSlop(moveDirection,out var surfaceNormal);
            m_MovementDirection = GetSlopMovementDirection(m_MovementDirection,surfaceNormal);
            if (!CanAnimationMotion(m_MovementDirection,moveSpeed)) return;
            CurrentMovementVelocity.Value =
                isLerp ? GetCurrentMovementSpeedLerp(moveSpeed) : moveSpeed;
            
            //缓存5帧的水平速度
            m_AvarageVelocity = AvarageVel(CurrentMovementVelocity.Value * m_MovementDirection);
            m_CC.Move(Time.fixedDeltaTime * CurrentMovementVelocity.Value * m_MovementDirection);
        }

        /// <summary>
        /// 水平速度，直接赋值
        /// 这个原本给RootMotion使用的。包含了垂直方向的速度
        /// </summary>
        /// <param name="moveDirection"></param>
        public virtual void CharacterMoveInterface(Vector3 moveDirection)
        {
            var speed = m_MovementDirection.magnitude;
            m_MovementDirection = ResetVelocityOnSlop(moveDirection, out var surfaceNormal);
            m_MovementDirection = GetSlopMovementDirection(m_MovementDirection, surfaceNormal);
            if (!CanAnimationMotion(m_MovementDirection, speed)) return;
            CurrentMovementVelocity.Value = speed;
            // Debug.Log(m_MovementDirection.magnitude);
            //缓存5帧的水平速度
            m_AvarageVelocity = AvarageVel(CurrentMovementVelocity.Value * m_MovementDirection);
            m_CC.Move(Time.deltaTime * CurrentMovementVelocity.Value * m_MovementDirection);
        }

        /// <summary>
        /// 垂直速度
        /// </summary>
        public virtual void CharacterVerticalVelocityInterface(float moveSpeed = 0f)
        {
            CurrentVerticalVelocity.Value += moveSpeed;
            m_VerticalDirection.Set(0, CurrentVerticalVelocity.Value, 0);
            m_CC.Move(m_VerticalDirection * Time.fixedDeltaTime);
        }

        /// <summary>
        /// 模拟重力
        /// </summary>
        public virtual void SimulateChracterGravity()
        {
            //如果不使用重力，那么就不要做下面的了
            if (!m_IsUseGravity) return;
            CharacterIsOnGround = GroundDetecion();
            CharacterHeadImpact = HeadImpactCheck();
            
            if (CharacterIsOnGround)
            {
                IsFalling = false;
                m_FallOutDeltaTimer = m_FallOutDeltaTime;

                if (CurrentVerticalVelocity.Value  < 0)
                {
                    //固定速度，防止第二次跳跃或者下坠速度叠加
                    CurrentVerticalVelocity.Value = -.5f;
                }
            }
            else
            {
                //碰到头了，直接下落
                if (CharacterHeadImpact)
                {
                    Debug.Log("碰到头了，有点疼");
                    CurrentVerticalVelocity.Value = -2.5f;
                }
                //处理地形导致的下坠，这个时间很短。不需要管他
                if (!CharacterHeadImpact && m_FallOutDeltaTimer >= 0)
                {
                    m_FallOutDeltaTimer -= Time.deltaTime;
                }
                else
                {
                    //正在坠落
                    IsFalling = true;
                    //限制下坠速度
                    if (CurrentVerticalVelocity.Value < m_VerticalSpeedClamp.y &&
                        CurrentVerticalVelocity.Value > m_VerticalSpeedClamp.x)
                    {
                        //倍重力，下降太慢手感不好
                        CurrentVerticalVelocity.Value += CharacterGravity * Time.fixedDeltaTime;
                    }
                }
            }
        }
        
        /// <summary>
        /// 是否使用重力
        /// </summary>
        /// <param name="useGravity"></param>
        public void ChangeGravityMode(bool useGravity)
        {
            m_IsUseGravity = useGravity;
            IsFalling = false;
            CurrentVerticalVelocity.Value = m_IsUseGravity ? -.5f : 0;
        }
        
        public void IgnoreGravity(float ignoreTime)
        {
            m_IsUseGravity = false;
            IsFalling = false;
            CurrentVerticalVelocity.Value = 0;
            IgnoreGravityTimer = ignoreTime;
        }

        /// <summary>
        /// 对当前移动速度进行插值计算
        /// </summary>
        /// <param name="desiredMoveSpeed"></param>
        public float GetCurrentMovementSpeedLerp(float desiredMoveSpeed)
        {
            var currentMovementSpeed = Mathf.Lerp(CurrentMovementVelocity.Value, desiredMoveSpeed,
                DevelopmentToos.UnTetheredLerp(
                    //根据加减速采用不痛插值平滑时间
                    CurrentMovementVelocity.Value < desiredMoveSpeed
                    ? StableMovementSharpnessUp
                    : StableMovementSharpnessDown));
            if (Mathf.Abs(desiredMoveSpeed - currentMovementSpeed) <= InterpolateEpsilon)
            {
                currentMovementSpeed = desiredMoveSpeed;
            }
            return currentMovementSpeed;
        }

        /// <summary>
        /// 对目标速度进行公式计算
        /// </summary>
        /// <param name="desiredMoveSpeed"></param>
        /// <param name="acceleration"></param>
        /// <returns></returns>
        public float AccelerateToTargetSpeed(float desiredMoveSpeed, float acceleration)
        {
            return CurrentMovementVelocity.Value + acceleration * Time.fixedDeltaTime;
        }


        protected virtual void Update()
        {
            //忽视重力
            if (IgnoreGravityTimer >= 0f)
            {
                IgnoreGravityTimer -= Time.deltaTime;
                if (IgnoreGravityTimer <= 0f)
                {
                    m_IsUseGravity = true;
                }
            }
        }

        /// <summary>
        /// 绘制地面检测
        /// </summary>
        private void OnDrawGizmosSelected()
        {
            if (CharacterIsOnGround)
            {
                Gizmos.color = Color.green;
            }
            else
            {
                Gizmos.color = Color.red;
            }

            //地面检测
            var position = transform.position;
            var groundDetectionPos = new Vector3(position.x,
                position.y - m_GroundDetectionOnPositionOffset, position.z);
            Gizmos.DrawWireSphere(groundDetectionPos, m_DetectionRange);
            
            if (CharacterHeadImpact)
            {
                Gizmos.color = Color.red;
            }
            else
            {
                Gizmos.color = Color.green;
            }
            //碰头检测
            var headDetectionPos = new Vector3(position.x,
                position.y + m_HeadDetectionOnPositionOffset, position.z);
            Gizmos.DrawWireSphere(headDetectionPos, m_HeadDetectionRange);
        }
    }
}
